package com.javabi.htmlbuilder.html;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import com.javabi.htmlbuilder.html.element.Anchor;
import com.javabi.htmlbuilder.html.element.Bold;
import com.javabi.htmlbuilder.html.element.Center;
import com.javabi.htmlbuilder.html.element.Div;
import com.javabi.htmlbuilder.html.element.HorizontalRule;
import com.javabi.htmlbuilder.html.element.Italic;
import com.javabi.htmlbuilder.html.element.LineBreak;
import com.javabi.htmlbuilder.html.element.Paragraph;
import com.javabi.htmlbuilder.html.element.PreformattedText;
import com.javabi.htmlbuilder.html.element.Span;
import com.javabi.htmlbuilder.html.element.content.Text;
import com.javabi.htmlbuilder.html.element.form.Form;
import com.javabi.htmlbuilder.html.element.form.Hidden;
import com.javabi.htmlbuilder.html.element.form.Password;
import com.javabi.htmlbuilder.html.element.form.Select;
import com.javabi.htmlbuilder.html.element.form.Submit;
import com.javabi.htmlbuilder.html.element.form.TextArea;
import com.javabi.htmlbuilder.html.element.form.TextBox;
import com.javabi.htmlbuilder.html.element.form.Form.Method;
import com.javabi.htmlbuilder.html.element.header.Header1;
import com.javabi.htmlbuilder.html.element.header.Header2;
import com.javabi.htmlbuilder.html.element.header.Header3;
import com.javabi.htmlbuilder.html.element.header.Header4;
import com.javabi.htmlbuilder.html.element.header.Header5;
import com.javabi.htmlbuilder.html.element.header.Header6;
import com.javabi.htmlbuilder.html.element.list.OrderedList;
import com.javabi.htmlbuilder.html.element.list.UnorderedList;
import com.javabi.htmlbuilder.html.element.table.Table;

/**
 * An HTML Element List.
 */
public abstract class HTMLElementList<H extends HTMLElement<H>> extends HTMLElement<H> {

	/** The content list. */
	private List<HTMLContent> contentList = null;

	/**
	 * Add the given content as a child of this content.
	 * @param content the content to add.
	 */
	public void add(HTMLContent content) {
		if (content == null) {
			throw new NullPointerException();
		}
		if (content == this) {
			throw new IllegalArgumentException("cannot add this content to itself");
		}
		if (contentList == null) {
			contentList = new ArrayList<HTMLContent>();
		}
		contentList.add(content);
	}

	/**
	 * Add a non breaking space.
	 */
	public void nbsp() {
		add(HTMLEntity.NBSP);
	}

	/**
	 * Add text content.
	 * @param text the text.
	 * @return this element.
	 */
	@SuppressWarnings("unchecked")
	public H text(Object text) {
		if (text == null) {
			throw new NullPointerException();
		}
		Text element = new Text(text.toString());
		add(element);
		return (H) this;
	}

	/**
	 * Add a header 1 element.
	 * @return the element.
	 */
	public Header1 h1() {
		Header1 header = new Header1();
		add(header);
		return header;
	}

	/**
	 * Add a header 2 element.
	 * @return the element.
	 */
	public Header2 h2() {
		Header2 header = new Header2();
		add(header);
		return header;
	}

	/**
	 * Add a header 3 element.
	 * @return the element.
	 */
	public Header3 h3() {
		Header3 header = new Header3();
		add(header);
		return header;
	}

	/**
	 * Add a header 4 element.
	 * @return the element.
	 */
	public Header4 h4() {
		Header4 header = new Header4();
		add(header);
		return header;
	}

	/**
	 * Add a header 5 element.
	 * @return the element.
	 */
	public Header5 h5() {
		Header5 header = new Header5();
		add(header);
		return header;
	}

	/**
	 * Add a header 6 element.
	 * @return the element.
	 */
	public Header6 h6() {
		Header6 header = new Header6();
		add(header);
		return header;
	}

	/**
	 * Add a header 1 element.
	 * @param text the text
	 * @return the element.
	 */
	@SuppressWarnings("unchecked")
	public H h1(Object text) {
		Header1 header = new Header1();
		header.text(text);
		add(header);
		return (H) this;
	}

	/**
	 * Add a header 2 element.
	 * @param text the text
	 * @return the element.
	 */
	@SuppressWarnings("unchecked")
	public H h2(Object text) {
		Header2 header = new Header2();
		header.text(text);
		add(header);
		return (H) this;
	}

	/**
	 * Add a header 3 element.
	 * @param text the text
	 * @return the element.
	 */
	@SuppressWarnings("unchecked")
	public H h3(Object text) {
		Header3 header = new Header3();
		header.text(text);
		add(header);
		return (H) this;
	}

	/**
	 * Add a header 4 element.
	 * @param text the text
	 * @return the element.
	 */
	@SuppressWarnings("unchecked")
	public H h4(Object text) {
		Header4 header = new Header4();
		header.text(text);
		add(header);
		return (H) this;
	}

	/**
	 * Add a header 5 element.
	 * @param text the text
	 * @return the element.
	 */
	@SuppressWarnings("unchecked")
	public H h5(Object text) {
		Header5 header = new Header5();
		header.text(text);
		add(header);
		return (H) this;
	}

	/**
	 * Add a header 6 element.
	 * @param text the text
	 * @return the element.
	 */
	@SuppressWarnings("unchecked")
	public H h6(Object text) {
		Header6 header = new Header6();
		header.text(text);
		add(header);
		return (H) this;
	}

	/**
	 * Add a header 1 element.
	 * @return the element.
	 */
	public Header1 header1() {
		return h1();
	}

	/**
	 * Add a header 2 element.
	 * @return the element.
	 */
	public Header2 header2() {
		return h2();
	}

	/**
	 * Add a header 3 element.
	 * @return the element.
	 */
	public Header3 header3() {
		return h3();
	}

	/**
	 * Add a header 4 element.
	 * @return the element.
	 */
	public Header4 header4() {
		return h4();
	}

	/**
	 * Add a header 5 element.
	 * @return the element.
	 */
	public Header5 header5() {
		return h5();
	}

	/**
	 * Add a header 6 element.
	 * @return the element.
	 */
	public Header6 header6() {
		return h6();
	}

	/**
	 * Add a header 1 element.
	 * @param text the text.
	 * @return the element.
	 */
	public H header1(Object text) {
		return h1(text);
	}

	/**
	 * Add a header 2 element.
	 * @param text the text.
	 * @return the element.
	 */
	public H header2(Object text) {
		return h2(text);
	}

	/**
	 * Add a header 3 element.
	 * @param text the text.
	 * @return the element.
	 */
	public H header3(Object text) {
		return h3(text);
	}

	/**
	 * Add a header 4 element.
	 * @param text the text.
	 * @return the element.
	 */
	public H header4(Object text) {
		return h4(text);
	}

	/**
	 * Add a header 5 element.
	 * @param text the text.
	 * @return the element.
	 */
	public H header5(Object text) {
		return h5(text);
	}

	/**
	 * Add a header 6 element.
	 * @param text the text.
	 * @return the element.
	 */
	public H header6(Object text) {
		return h6(text);
	}

	/**
	 * Add a center element.
	 * @return the element.
	 */
	public Center center() {
		Center center = new Center();
		add(center);
		return center;
	}

	/**
	 * Add a bold element.
	 * @return the element.
	 */
	public Bold b() {
		Bold bold = new Bold();
		add(bold);
		return bold;
	}

	/**
	 * Add bold text.
	 * @param text the text.
	 * @return this element.
	 */
	@SuppressWarnings("unchecked")
	public H b(Object text) {
		if (text == null) {
			throw new NullPointerException();
		}
		Bold bold = b();
		bold.text(text);
		return (H) this;
	}

	/**
	 * Add bold text.
	 * @param text the text.
	 * @return this element.
	 */
	public H bold(Object text) {
		return b(text);
	}

	/**
	 * Add an ordered list.
	 * @return the element.
	 */
	public OrderedList ol() {
		OrderedList list = new OrderedList();
		add(list);
		return list;
	}

	/**
	 * Add an ordered list.
	 * @return the element.
	 */
	public OrderedList orderedList() {
		return ol();
	}

	/**
	 * Add an unordered list.
	 * @return the element.
	 */
	public UnorderedList ul() {
		UnorderedList list = new UnorderedList();
		add(list);
		return list;
	}

	/**
	 * Add an unordered list.
	 * @return the element.
	 */
	public UnorderedList unorderedList() {
		return ul();
	}

	/**
	 * Add a preformatted text element.
	 * @return the element.
	 */
	public PreformattedText pre() {
		PreformattedText pre = new PreformattedText();
		add(pre);
		return pre;
	}

	/**
	 * Add a preformatted text element.
	 * @param text the text.
	 * @return the element.
	 */
	@SuppressWarnings("unchecked")
	public H pre(Object text) {
		if (text == null) {
			throw new NullPointerException();
		}
		PreformattedText pre = pre();
		pre.text(text);
		return (H) this;
	}

	/**
	 * Add a preformatted text element.
	 * @return the element.
	 */
	public PreformattedText preformattedText() {
		return pre();
	}

	/**
	 * Add a preformatted text element.
	 * @param text the text.
	 * @return the element.
	 */
	public H preformattedText(Object text) {
		return pre(text);
	}

	/**
	 * Add a paragraph element.
	 * @return the element.
	 */
	public Paragraph p() {
		Paragraph paragraph = new Paragraph();
		add(paragraph);
		return paragraph;
	}

	/**
	 * Add a bold element.
	 * @return the element.
	 */
	public Bold bold() {
		return b();
	}

	/**
	 * Add italic text.
	 * @param text the text.
	 * @return this element.
	 */
	@SuppressWarnings("unchecked")
	public H i(Object text) {
		if (text == null) {
			throw new NullPointerException();
		}
		Italic italic = i();
		italic.text(text);
		return (H) this;
	}

	/**
	 * Add an italic element.
	 * @return the element.
	 */
	public Italic i() {
		Italic italic = new Italic();
		add(italic);
		return italic;
	}

	/**
	 * Add an italic element.
	 * @return the element.
	 */
	public Italic italic() {
		return i();
	}

	/**
	 * Add an anchor element.
	 * @return the element.
	 */
	public Anchor a() {
		Anchor anchor = new Anchor();
		add(anchor);
		return anchor;
	}

	/**
	 * Add an anchor element.
	 * @return the element.
	 */
	public Anchor anchor() {
		return a();
	}

	/**
	 * Add an anchor element.
	 * @param href the reference.
	 * @return the element.
	 */
	public Anchor a(String href) {
		if (href == null) {
			throw new NullPointerException();
		}
		Anchor anchor = new Anchor(href);
		anchor.text(href);
		add(anchor);
		return anchor;
	}

	/**
	 * Add an anchor element.
	 * @param href the reference.
	 * @param text the text in the anchor.
	 * @return the element.
	 */
	@SuppressWarnings("unchecked")
	public H a(String href, Object text) {
		if (href == null) {
			throw new NullPointerException();
		}
		Anchor anchor = new Anchor(href);
		anchor.text(text);
		add(anchor);
		return (H) this;
	}

	/**
	 * Add a table element.
	 * @return the element.
	 */
	public Table table() {
		Table table = new Table();
		add(table);
		return table;
	}

	/**
	 * Add a line break.
	 * @return this element.
	 */
	@SuppressWarnings("unchecked")
	public H br() {
		add(new LineBreak());
		return (H) this;
	}

	/**
	 * Add a line break.
	 * @return this element.
	 */
	public H lineBreak() {
		return br();
	}

	/**
	 * Add a horizontal rule.
	 * @return this element.
	 */
	@SuppressWarnings("unchecked")
	public H hr() {
		add(new HorizontalRule());
		return (H) this;
	}

	/**
	 * Add a horizontal rule.
	 * @return this element.
	 */
	public H horizontalRule() {
		return hr();
	}

	/**
	 * Add a span.
	 * @return this element.
	 */
	@SuppressWarnings("unchecked")
	public H span() {
		add(new Span());
		return (H) this;
	}

	/**
	 * Add a division.
	 * @return this element.
	 */
	@SuppressWarnings("unchecked")
	public H div() {
		add(new Div());
		return (H) this;
	}

	/**
	 * Add a division.
	 * @return this element.
	 */
	public H division() {
		return div();
	}

	/**
	 * Add a form element.
	 * @param name the name.
	 * @return the element.
	 */
	public Form form(String name) {
		Form form = new Form(name);
		add(form);
		return form;
	}

	/**
	 * Add a form element.
	 * @param name the name.
	 * @param method the method.
	 * @return the element.
	 */
	public Form form(String name, Method method) {
		Form form = new Form(name, method);
		add(form);
		return form;
	}

	/**
	 * Add a form text area.
	 * @param name the text area name.
	 * @return the text area.
	 */
	public TextArea textarea(String name) {
		TextArea textarea = new TextArea(name);
		add(textarea);
		return textarea;
	}

	/**
	 * Add a form text area.
	 * @param name the text area name.
	 * @param rows the number of rows.
	 * @param cols the number of columns.
	 * @return the text area.
	 */
	public TextArea textarea(String name, int rows, int cols) {
		TextArea textarea = new TextArea(name, rows, cols);
		add(textarea);
		return textarea;
	}

	/**
	 * Add a form text box.
	 * @param name the text box name.
	 * @return the text box.
	 */
	public TextBox textbox(String name) {
		TextBox textBox = new TextBox(name);
		add(textBox);
		return textBox;
	}

	/**
	 * Add a form text box.
	 * @param name the text box name.
	 * @param value the text box value.
	 * @return the text box.
	 */
	public TextBox textbox(String name, Object value) {
		TextBox textBox = new TextBox(name, value);
		add(textBox);
		return textBox;
	}

	/**
	 * Add a form hidden input.
	 * @param name the hidden name.
	 * @param value the hidden value.
	 * @return the hidden.
	 */
	public Hidden hidden(String name, Object value) {
		Hidden hidden = new Hidden(name, value);
		add(hidden);
		return hidden;
	}

	/**
	 * Add a form password box.
	 * @param name the password box name.
	 * @return the password box.
	 */
	public Password password(String name) {
		Password password = new Password(name);
		add(password);
		return password;
	}

	/**
	 * Add a form submit.
	 * @param value the value.
	 * @return the submit.
	 */
	public Submit submit(String value) {
		Submit submit = new Submit(value);
		add(submit);
		return submit;
	}

	/**
	 * Add a form select (drop-down).
	 * @param name the name.
	 * @return the submit.
	 */
	public Select select(String name) {
		Select select = new Select();
		select.setName(name);
		add(select);
		return select;
	}

	/**
	 * Add a form select (drop-down).
	 * @param name the name.
	 * @param values the values.
	 * @return the submit.
	 */
	public Select select(String name, Collection<? extends Object> values) {
		Select select = new Select();
		select.setName(name);
		for (Object value : values) {
			select.option(value).text(value);
		}
		add(select);
		return select;
	}

	/**
	 * Add a form select (drop-down).
	 * @param name the name.
	 * @param values the values.
	 * @param displayValues the display values.
	 * @return the submit.
	 */
	public Select select(String name, Collection<? extends Object> values, Collection<? extends Object> displayValues) {
		if (values.size() != displayValues.size()) {
			throw new IllegalArgumentException(values.size() + "!=" + displayValues.size());
		}
		Select select = new Select();
		select.setName(name);
		Iterator<?> valueIterator = values.iterator();
		Iterator<?> displayIterator = displayValues.iterator();
		while (valueIterator.hasNext() && displayIterator.hasNext()) {
			select.option(valueIterator.next()).text(displayIterator.next());
		}
		add(select);
		return select;
	}

	/**
	 * Add a form select (drop-down).
	 * @param name the name.
	 * @param values the values.
	 * @return the submit.
	 */
	public Select select(String name, Object... values) {
		Select select = new Select();
		select.setName(name);
		for (Object value : values) {
			select.option(value).text(value);
		}
		add(select);
		return select;
	}

	/**
	 * Add a form select (drop-down).
	 * @param name the name.
	 * @param values the values.
	 * @return the submit.
	 */
	public <E extends Enum<E>> Select select(String name, Class<E> values) {
		Select select = new Select(name, values);
		add(select);
		return select;
	}

	/**
	 * Add a form select (drop-down).
	 * @param name the name.
	 * @param values the values.
	 * @param selectedValue the selected value.
	 * @return the submit.
	 */
	public <E extends Enum<E>> Select select(String name, Class<E> values, E selectedValue) {
		Select select = new Select(name, values, selectedValue);
		add(select);
		return select;
	}

	/**
	 * Append attributes to the given html builder.
	 * @param html the html builder.
	 * @return the html builder.
	 */
	public HTMLBuilder appendAttributesTo(HTMLBuilder html) {
		return html;
	}

	/**
	 * Returns true to format this element.
	 * @return true to format this element.
	 */
	public boolean format() {
		if (contentList != null) {
			for (HTMLContent content : contentList) {
				if (content.format()) {
					return true;
				}
			}
		}
		return false;
	}

	/**
	 * Append this to the given html builder.
	 * @param html the html builder.
	 * @return the html builder.
	 */
	public HTMLBuilder appendTo(HTMLBuilder html) {
		html.open(this, true);
		appendInlineStyleAttributesTo(html);
		appendAttributesTo(html);
		if (contentList != null) {
			for (HTMLContent content : contentList) {
				content.appendTo(html);
			}
		}
		html.close(this);
		return html;
	}

}
